/**
 * @file    gt98xx_drv_iwdt.h
 * @author  Giantec-Semi ATE
 * @brief   Header file of IWDT driver module.
 * @version 0.1
 * 
 * @copyright Copyright (c) 2021 Giantec-Semi
 */

#ifndef GT98XX_DRIVERS_GT98XX_DRV_IWDT_H_
#define GT98XX_DRIVERS_GT98XX_DRV_IWDT_H_

#ifdef __cplusplus
  extern "C" {
#endif /* __cplusplus */

#include "gt98xx_drv_def.h"
#include "gt98xx_drv_conf.h"

/**
 * @addtogroup GT9881_Drivers
 * @{
 */

/**
 * @defgroup IWDT_DRV IWDT Drivers
 * @{
 */

/**
 * @defgroup IWDT_DRV_Exported_Types IWDT Drivers Exported Types
 * @{
 */

/**
 * @struct DrvIwdtInitTypedef
 * @brief IWDT drivers init structure definition
 */
typedef struct tagDrvIwdtInitTypedef {
  uint32_t load_value;      ///< IWDT Load Value
  uint32_t time_out;        ///< IWDT Time out range
  uint32_t action;          ///< IWDT Action
  uint32_t divisor;         ///< IWDT Divisor
} DrvIwdtInitTypedef;
/** @} IWDT_DRV_Exported_Types */

/**
 * @defgroup IWDT_DRV_Exported_Constants IWDT Drivers Exported Constants
 * @{
 */

/**
 * @defgroup IWDT_DRV_EC_Action IWDT Action
 * @{
 */
#define DRV_IWDT_ACTION_RST                 (0x0U << IWDT_TCR_ACTION_Pos)      ///< When IWDT count reaches 0, a reset output is generated
#define DRV_IWDT_ACTION_IT                  (0x1U << IWDT_TCR_ACTION_Pos)     ///< When IWDT count reaches 0, an interrupt request is generated
/** @} IWDT_DRV_EC_Action */

/**
 * @defgroup IWDT_DRV_EC_Prescaler IWDT Prescaler
 * @{
 */
#define DRV_IWDT_PRESCALER_2        (0x00U << IWDT_TCR_DIVISOR_Pos)   ///< Divided by 2
#define DRV_IWDT_PRESCALER_4        (0x01U << IWDT_TCR_DIVISOR_Pos)   ///< Divided by 4
#define DRV_IWDT_PRESCALER_8        (0x02U << IWDT_TCR_DIVISOR_Pos)   ///< Divided by 8
#define DRV_IWDT_PRESCALER_16       (0x03U << IWDT_TCR_DIVISOR_Pos)   ///< Divided by 16
#define DRV_IWDT_PRESCALER_32       (0x04U << IWDT_TCR_DIVISOR_Pos)   ///< Divided by 32
#define DRV_IWDT_PRESCALER_64       (0x05U << IWDT_TCR_DIVISOR_Pos)   ///< Divided by 64
#define DRV_IWDT_PRESCALER_128      (0x06U << IWDT_TCR_DIVISOR_Pos)   ///< Divided by 128
#define DRV_IWDT_PRESCALER_256      (0x07U << IWDT_TCR_DIVISOR_Pos)   ///< Divided by 256
/** @} IWDT_DRV_EC_Prescaler */

/**
 * @defgroup IWDT_DRV_EC_Timeout IWDT Timeout
 * @{
 */
#define DRV_IWDT_TIMEOUT_16         (0x00U << IWDT_TCR_TIMEOUT_RANGE_Pos)   ///< 16 of WDT Counter clock cycles
#define DRV_IWDT_TIMEOUT_32         (0x01U << IWDT_TCR_TIMEOUT_RANGE_Pos)   ///< 32 of WDT Counter clock cycles
#define DRV_IWDT_TIMEOUT_64         (0x02U << IWDT_TCR_TIMEOUT_RANGE_Pos)   ///< 64 of WDT Counter clock cycles
#define DRV_IWDT_TIMEOUT_128        (0x03U << IWDT_TCR_TIMEOUT_RANGE_Pos)   ///< 128 of WDT Counter clock cycles
#define DRV_IWDT_TIMEOUT_256        (0x04U << IWDT_TCR_TIMEOUT_RANGE_Pos)   ///< 256 of WDT Counter clock cycles
#define DRV_IWDT_TIMEOUT_512        (0x05U << IWDT_TCR_TIMEOUT_RANGE_Pos)   ///< 512 of WDT Counter clock cycles
#define DRV_IWDT_TIMEOUT_1k         (0x06U << IWDT_TCR_TIMEOUT_RANGE_Pos)   ///< 1K of WDT Counter clock cycles
/** @} IWDT_DRV_EC_Timeout */

/** @} IWDT_DRV_Exported_Constants */

/**
 * @defgroup IWDT_DRV_Exported_Functions IWDT Drivers Exported Functions
 * @{
 */

/**
 * @defgroup IWDT_DRV_EF_Configuration IWDT Drivers Configuration Exported Functions
 * @{
 */

/**
 * @fn __STATIC_INLINE void DrvIwdtEnable(IwdtTypedef* iwdt_instance)
 * @brief Start the Independent Watchdog
 * 
 * @param[in] iwdt_instance IWDT instance
 */
__STATIC_INLINE void DrvIwdtEnable(IwdtTypedef* iwdt_instance) {
  SET_BIT(iwdt_instance->TCR, IWDT_TCR_ENABLE);
}

/**
 * @fn __STATIC_INLINE void DrvIwdtDisable(IwdtTypedef* iwdt_instance)
 * @brief Disable Watchdog Timer
 * 
 * @param[in] iwdt_instance IWDT instance
 */
__STATIC_INLINE void DrvIwdtDisable(IwdtTypedef* iwdt_instance) {
  CLEAR_BIT(iwdt_instance->TCR, IWDT_TCR_ENABLE);
}

/**
 * @fn __STATIC_INLINE uint32_t DrvIwdtIsEnabled(IwdtTypedef* iwdt_instance)
 * @brief Check if IWDT is enabled
 * 
 * @param[in] iwdt_instance IWDT instance
 * @return IWDT is enabled or disabled
 * @retval 0 Disabled
 * @retval 1 Enabled
 */
__STATIC_INLINE uint32_t DrvIwdtIsEnabled(IwdtTypedef* iwdt_instance) {
  return READ_BIT(iwdt_instance->TCR, IWDT_TCR_ENABLE) >> IWDT_TCR_ENABLE_Pos;
}

/**
 * @fn __STATIC_INLINE void DrvIwatSetAction(IwdtTypedef* iwdt_instance, uint32_t action)
 * @brief Set the action when timer count reaches zero
 * 
 * @param[in] iwdt_instance IWDT instance
 * @param[in] action This parameter can be one of the following values:
 *            @arg @ref DRV_IWDT_ACTION_RST
 *            @arg @ref DRV_IWDT_ACTION_IT
 */
__STATIC_INLINE void DrvIwatSetAction(IwdtTypedef* iwdt_instance, uint32_t action) {
  MODIFY_REG(iwdt_instance->TCR, IWDT_TCR_ACTION, action);
}

/**
 * @fn __STATIC_INLINE uint32_t DrvIwdtGetAction(IwdtTypedef* iwdt_instance)
 * @brief Get the action when timer count reaches zero
 * 
 * @param[in] iwdt_instance IWDT instance
 * @return The action when IWDT count reaches zero
 * @retval DRV_IWDT_Action_RST When IWDT count reaches 0, a reset output is generated
 * @retval DRV_IWDT_Action_IT When IWDT count reaches 0, an interrupt request is generated
 */
__STATIC_INLINE uint32_t DrvIwdtGetAction(IwdtTypedef* iwdt_instance) {
  return (uint32_t)READ_BIT(iwdt_instance->TCR, IWDT_TCR_ACTION);
}

/**
 * @fn __STATIC_INLINE void DrvIwdtSetCounterValue(IwdtTypedef* iwdt_instance, uint32_t value)
 * @brief Set Watchdog Timer load value
 * @note  Only write 0x76 to WDT_CRR, the Load value can restart the counter values
 * 
 * @param[in] iwdt_instance IWDT instance
 * @param[in] value Watchdog Timer load value
 */
__STATIC_INLINE void DrvIwdtSetCounterValue(IwdtTypedef* iwdt_instance, uint32_t value) {
  MODIFY_REG(iwdt_instance->LDR, IWDT_LDR_LOAD_VALUE, value << IWDT_LDR_LOAD_VALUE_Pos);
  WRITE_REG(iwdt_instance->CRR, IWDT_CNT_RESTART_VALUE);
}

/**
 * @fn __STATIC_INLINE uint32_t DrvIwdtGetCounterValue(IwdtTypedef* iwdt_instance)
 * @brief Get the current value of IWDT
 * 
 * @param[in] iwdt_instance IWDT instance
 * @return Current value of IWDT
 */
__STATIC_INLINE uint32_t DrvIwdtGetCounterValue(IwdtTypedef* iwdt_instance) {
  return (uint32_t) READ_REG(iwdt_instance->CNT);
}

/**
 * @fn __STATIC_INLINE void DrvIwdtReset(IwdtTypedef* iwdt_instance)
 * @brief Reset value of IWDT
 * 
 * @param[in] iwdt_instance IWDT instance
 */
__STATIC_INLINE void DrvIwdtReset(IwdtTypedef* iwdt_instance) {
  WRITE_REG(iwdt_instance->CRR, IWDT_CNT_RESTART_VALUE);
}

/**
 * @fn __STATIC_INLINE void DrvIwdtSetTimeoutReset(IwdtTypedef* iwdt_instance, uint32_t timeout_range)
 * @brief Set IWDT timeout range
 * 
 * @param[in] iwdt_instance IWDT instance
 * @param[in] timeout_range This parameter can be one of the following values:
 *            @arg @ref DRV_IWDT_TIMEOUT_16
 *            @arg @ref DRV_IWDT_TIMEOUT_32
 *            @arg @ref DRV_IWDT_TIMEOUT_64
 *            @arg @ref DRV_IWDT_TIMEOUT_128
 *            @arg @ref DRV_IWDT_TIMEOUT_256
 *            @arg @ref DRV_IWDT_TIMEOUT_512
 *            @arg @ref DRV_IWDT_TIMEOUT_1k
 */
__STATIC_INLINE void DrvIwdtSetTimeoutReset(IwdtTypedef* iwdt_instance, uint32_t timeout_range) {
  MODIFY_REG(iwdt_instance->TCR, IWDT_TCR_TIMEOUT_RANGE, timeout_range);
}

/**
 * @fn __STATIC_INLINE uint32_t DrvIwdtGetTimeoutReset(IwdtTypedef* iwdt_instance)
 * @brief Get IWDT timeout range
 * 
 * @param[in] iwdt_instance IWDT instance
 * @return IWDT timeout range
 * @retval DRV_IWDT_TIMEOUT_16 16 of WDT Counter clock cycles
 * @retval DRV_IWDT_TIMEOUT_32 32 of WDT Counter clock cycles
 * @retval DRV_IWDT_TIMEOUT_64 64 of WDT Counter clock cycles
 * @retval DRV_IWDT_TIMEOUT_128 128 of WDT Counter clock cycles
 * @retval DRV_IWDT_TIMEOUT_256 256 of WDT Counter clock cycles
 * @retval DRV_IWDT_TIMEOUT_512 512 of WDT Counter clock cycles
 * @retval DRV_IWDT_TIMEOUT_1k 1K of WDT Counter clock cycles
 */
__STATIC_INLINE uint32_t DrvIwdtGetTimeoutReset(IwdtTypedef* iwdt_instance) {
  return (uint32_t) READ_BIT(iwdt_instance->TCR, IWDT_TCR_TIMEOUT_RANGE);
}

/**
 * @fn __STATIC_INLINE void DrvIwdtSetPrescaler(IwdtTypedef* iwdt_instance, uint32_t prescaler)
 * @brief Set the prescaler of IWDt
 * 
 * @param[in] iwdt_instance IWDT instance
 * @param[in] prescaler This parameter can be one of the following values:
 *            @arg @ref DRV_IWDT_PRESCALER_2
 *            @arg @ref DRV_IWDT_PRESCALER_4
 *            @arg @ref DRV_IWDT_PRESCALER_8
 *            @arg @ref DRV_IWDT_PRESCALER_16
 *            @arg @ref DRV_IWDT_PRESCALER_32
 *            @arg @ref DRV_IWDT_PRESCALER_64
 *            @arg @ref DRV_IWDT_PRESCALER_128
 *            @arg @ref DRV_IWDT_PRESCALER_256
 */
__STATIC_INLINE void DrvIwdtSetPrescaler(IwdtTypedef* iwdt_instance, uint32_t prescaler) {
  MODIFY_REG(iwdt_instance->TCR, IWDT_TCR_DIVISOR, prescaler);
}

/**
 * @fn __STATIC_INLINE uint32_t DrvIwdtGetPrescaler(IwdtTypedef* iwdt_instance)
 * @brief Get the selected prescaler of IWDT
 * 
 * @param[in] iwdt_instance IWDT instance
 * @return The selected prescaler of IWDT
 * @retval DRV_IWDT_PRESCALER_2 Divided by 2
 * @retval DRV_IWDT_PRESCALER_4 Divided by 4
 * @retval DRV_IWDT_PRESCALER_8 Divided by 8
 * @retval DRV_IWDT_PRESCALER_16 Divided by 16
 * @retval DRV_IWDT_PRESCALER_32 Divided by 32
 * @retval DRV_IWDT_PRESCALER_64 Divided by 64
 * @retval DRV_IWDT_PRESCALER_128 Divided by 128
 * @retval DRV_IWDT_PRESCALER_256 Divided by 256
 */
__STATIC_INLINE uint32_t DrvIwdtGetPrescaler(IwdtTypedef* iwdt_instance) {
  return (uint32_t) READ_BIT(iwdt_instance->TCR, IWDT_TCR_DIVISOR);
}

/** @} IWDT_DRV_EF_Configuration */

/**
 * @defgroup IWDT_DRV_EF_IT_Management IWDT Interrupt Management Exported Functions
 * @{
 */

/**
 * @fn __STATIC_INLINE void DrvIwdtClearIt(IwdtTypedef* iwdt_instance)
 * @brief Clear IWDT interrupt without restarting the watchdog counter
 * 
 * @param[in] iwdt_instance IWDT instance
 */
__STATIC_INLINE void DrvIwdtClearIt(IwdtTypedef* iwdt_instance) {
  SET_BIT(iwdt_instance->ISR, IWDT_ISR_INTSTAT);
}

/**
 * @fn __STATIC_INLINE uint32_t DrvIwdtIsItActive(IwdtTypedef* iwdt_instance)
 * @brief Get the interrupt status of IWDT
 * 
 * @param[in] iwdt_instance IWDT instance
 * @return Interrupt status of IWDT
 * @retval 0 Interrupt is inactive
 * @retval 1 Interrupt is active
 */
__STATIC_INLINE uint32_t DrvIwdtIsItActive(IwdtTypedef* iwdt_instance) {
  return (uint32_t)READ_BIT(iwdt_instance->ISR, IWDT_ISR_INTSTAT) >> IWDT_ISR_INTSTAT_Pos;
}

/** @} IWDT_DRV_EF_IT_Management */

/**
 * @defgroup IWDT_DRV_EF_Init IWDT Drivers Initialization and De-initialization Exported Functions
 * @{
 */
/**
 * @fn ErrorStatus DrvIwdtDeInit(IwdtTypedef* iwdt_instance)
 * @brief De-initialize IWDT registers
 * 
 * @param[in] iwdt_instance IWDT instance
 * @return An ErrorStatus enumeration value
 * @retval kSuccess IWDT registers are de-initialized
 * @retval kError IWDT registers are not de-initialized
 */
ErrorStatus DrvIwdtDeInit(IwdtTypedef* iwdt_instance);

/**
 * @fn ErrorStatus DrvIwdtInit(IwdtTypedef* iwdt_instance, DrvIwdtInitTypedef* iwdt_init_struct)
 * @brief Initialize IWDT registers according to the specified parameters in @ref DrvIwdtInitTypedef
 * 
 * @param[in] iwdt_instance IWDT instance
 * @param[in] iwdt_init_struct pointer to a @ref DrvIwdtInitTypedef structure that
 *            contians the configuration information for the specified IWDT peripheral
 * @return An ErrorStatus enumeration value
 * @retval kSuccess IWDT registers are de-initialized
 * @retval kError IWDT registers are not de-initialized
 */
ErrorStatus DrvIwdtInit(IwdtTypedef* iwdt_instance, DrvIwdtInitTypedef* iwdt_init_struct);

/**
 * @fn void DrvIwdtStructInit(DrvIwdtInitTypedef* iwdt_init_struct, uint32_t value)
 * @brief Set each @ref DrvIwdtInitTypedef field to default value
 * 
 * @param[out] iwdt_init_struct pointer to a @ref DrvIwdtInitTypedef structure
 *             whose field will be set to default values
 * @param[in]  value iwdt value
 */
void DrvIwdtStructInit(DrvIwdtInitTypedef* iwdt_init_struct, uint32_t value);

/** @} IWDT_DRV_EF_Init */
/** @} IWDT_DRV_Exported_Functions */
/** @} IWDT_DRV */
/** @} GT9881_Drivers */

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* GT98XX_DRIVERS_GT98XX_DRV_IWDT_H_ */
